package rwt.server.core;

import java.io.IOException;

import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
import org.slf4j.*;

import rwt.server.*;
import rwt.server.util.CoreUtil;

/**
 * The client end of an open WebSocket connection. Provides methods for sending and receiving messages to and
 * from this client.
 */
public class WebClient implements IClient, OnTextMessage
{
	private static final Logger logger = LoggerFactory.getLogger(WebClient.class);
	
	/** Max time in ms to keep the Web Socket connection after the client has become idle. */
	private static final int MAX_IDLE_TIME_MILLIS = 30 * 60 * 1000; // 30 minutes
	
	/** The Jetty WebSocket connection to browser client. */
	private volatile Connection webConnection;
	
	/** Owning client manager. */
	private IClientManager manager;
	
	/** The associated user. */
	private User user;
	
	/** The number that identifies this client connection. Unique among all open clients. */
	public int id;
	
	public WebClient(IClientManager manager, int id, User user)
	{
		this.manager = manager;
		this.id = id;
		this.user = user;
	}
	
	@Override
	public User getUser()
	{
		return user;
	}
	
	/**
	 * Callback for when a WebSocket connection is opened.
	 * 
	 * @param connection The connection to WebSocket browser client.
	 */
	@Override
	public void onOpen(Connection connection)
	{
		webConnection = connection;
		connection.setMaxIdleTime(MAX_IDLE_TIME_MILLIS);
	}
	
	/**
	 * Callback for when a WebSocket connection is closed.
	 */
	@Override
	public void onClose(int closeCode, String message)
	{
		close();
	}
	
	/**
	 * Callback for when a WebSocket message is received.
	 */
	@Override
	public void onMessage(String text)
	{
		if ("[polo]".equals(text))
		{
			return;
		}
		
		logger.info(getUserName() + ": " + text);
		
		if ("[join]".equals(text))
		{
			manager.addClient(this);
			write(new WebMessages.PutSystemStatus(CoreUtil.getSystemStatus()));
			write(RailServer.getTrainManager().getPutAvailableTrains());
			write(new WebMessages.WelcomeMessage(getUser().getUserName()));
		}
		else
		{
			final String json = text;
			
			manager.submitTask(new Runnable() {
				@Override public void run()
				{
					try
					{
						BaseMessage message = RailServer.getMessageAdaptor().jsonToMessage(json);
						MessageType type = MessageType.fromId(message.id);
						MessageHandler<BaseMessage> handler = type.getHandler();
						
						if (handler != null)
						{
							handler.handle(message, WebClient.this);
						}
						else
						{
							logger.info("No handler for message ID " + message.id);
						}
					}
					catch (Exception e)
					{
						logger.warn("", e);
					}
				}
			});
			
			logger.info("-- Returning from onMessage");
		}
	}
	
	@Override
	public synchronized void write(String msg)
	{
		try
		{
			if (!"[marco]".equals(msg))
				logger.info("Server: " + msg);
			if (msg != null && !msg.isEmpty())
				webConnection.sendMessage(msg);
		}
		catch (Exception e)
		{
			logger.warn("", e);
			close();
		}
	}
	
	@Override
	public void write(BaseMessage message)
	{
		try
		{
			String json = RailServer.getMessageAdaptor().messageToJson(message);
			write(json);
		}
		catch (IOException e)
		{
			logger.warn("", e);
		}
	}
	
	@Override
	public synchronized void close()
	{
		logger.info("Closed connection to " + getUserName());
		
		manager.removeClient(this);
		webConnection.close();
	}
	
	private String getUserName()
	{
		return user.getUserName();
	}

	@Override
	public int getId()
	{
		return id;
	}
}
